package org.zn.note.jdk.others;

import java.util.concurrent.TimeUnit;

/**
 * 单例模式最佳实践
 * <p>
 * 懒汉式：指全局的单例实例在第一次被使用时才被构建。
 * 饿汉式：指全局的单例实例在类装载时构建。
 * <p>
 * Created by zn on 2018/11/20.
 */
public class Singleton {

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++)
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + ":" + Single5.getInstance().hashCode());
                }
            }, "Thread-" + i).start();
    }
}

// 假设单例都在构建这个实例对象
class DataSource {
    public DataSource() {
        try {
            TimeUnit.SECONDS.sleep(1); // 假设对象构造需要1秒，以便观察多线程下是否会是单例
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

// 懒汉1：
// 问题：多线程会有多实例
class Single1 {
    private static DataSource instance;

    public static DataSource getInstance() {
        if (instance == null) {
            instance = new DataSource();
        }
        return instance;
    }
}

// 懒汉2：是单例
// 问题：但是首次创建后，后面全都是顺序调用了。 为了解决1%的问题，用了100%的护盾。
class Single2 {
    private static DataSource instance;

    public static synchronized DataSource getInstance() {
        if (instance == null) {
            instance = new DataSource();
        }
        return instance;
    }
}

// 懒汉3：Double-Check
// 问题：可能获取到未初始化完成的实例对象
// instance = new DataSource(); 不是原子操作： 1、分配内存  2、实例化DataSource  3、instance指向分配的内存
// 当指令重排时，可能的执行顺序：  1  3  2
// 当执行完  1  3  时， 判断 instance == null 已经是 false 了，但其实 DataSource还没有实例化完
// 这时另外一个线程把 instance 拿走用了，结果报错了
//
// 原子操作（atomic）：一个操作不会被线程调度打断。
// 比如 m = 6：只有会两种状态，m = 执行前状态、m = 执行后状态，而不会有中间状态
// 比如 int m = 6：int m 是一个原子操作，m = 6 是一个原子操作，所以 “int m = 6”是有中间状态的，
//                 比如另外一个线程 m = 3，这样在执行一个操作“int m = 6”的过程中，是可能有 m = 3 的中间状态的！
// 指令重排：非原子操作，拆分成原子操作，在不影响执行结果的前提下，重排执行顺序（为了执行效率）
// 比如 int a = 8; int b = 9; int c = a + b;
// 正常执行顺序： 1、int a  2、a = 8  3、int b  4、b = 9  5、int c  6、a + b  7、c = 结果
// 重排后可能是： 1 3 5 2 4 6 7
class Single3 {
    private static DataSource instance;

    public static DataSource getInstance() {
        if (instance == null) { // 防止创建后，还是顺序调用，解决了Single2的问题
            synchronized (Single3.class) { // 防止多线程多实例，解决了Single1的问题
                if (instance == null) {
                    instance = new DataSource();
                }
            }
        }
        return instance;
    }
}

// 懒汉4：终极版 （加了 volatile）
class Single4 {
    private static volatile DataSource instance;

    public static DataSource getInstance() {
        if (instance == null) { // 防止创建后，还是顺序调用，解决了Single2的问题
            synchronized (Single3.class) { // 防止多线程多实例，解决了Single1的问题
                if (instance == null) {
                    instance = new DataSource();
                }
            }
        }
        return instance;
    }
}

// 饿汉1
// 缺点：1、初始化太早、浪费资源  2、不受控制，如果初始化需要依赖其他数据，无法保证其他数据已经准备好了
class Single5 {
    private static final DataSource INSTANCE = new DataSource();

    public static DataSource getInstance() {
        return INSTANCE;
    }
}

// 最佳实践1：静态内部类
class Single6 {

    private static class SingletonHolder { // 内部饿汉，保证单例
        private static final DataSource INSTANCE = new DataSource();
    }

    public static final DataSource getInstance() { // 对外懒汉，用时加载
        return SingletonHolder.INSTANCE;
    }
}

// 最佳实践2：枚举
// 调用：Single7.INSTANCE.fun()
enum Single7 {
    INSTANCE;

    public void fun() {
    }
}